package org.oslims.client.presenter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.oslims.client.AsyncCall;
import org.oslims.client.MainEventBus;
import org.oslims.client.service.AnalysisServiceAsync;
import org.oslims.client.service.DepartmentServiceAsync;
import org.oslims.client.service.ParameterServiceAsync;
import org.oslims.client.service.SampleTypeServiceAsync;
import org.oslims.client.uicomponents.SortableHeader;
import org.oslims.client.uicomponents.WidgetUtils;
import org.oslims.client.uicomponents.editbox.EditAnalysisBox;
import org.oslims.client.view.impl.AnalysisEditorViewImpl;
import org.oslims.domain.Analysis;
import org.oslims.domain.Department;
import org.oslims.domain.Parameter;
import org.oslims.domain.SampleType;

import com.google.gwt.cell.client.ButtonCell;
import com.google.gwt.cell.client.CheckboxCell;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.validation.client.InvalidConstraint;
import com.google.gwt.validation.client.interfaces.IValidator;
import com.google.gwt.view.client.ListView;
import com.google.gwt.view.client.Range;
import com.google.gwt.view.client.ListView.Delegate;
import com.mvp4g.client.annotation.InjectService;
import com.mvp4g.client.annotation.Presenter;
import com.mvp4g.client.presenter.BasePresenter;

@Presenter(view = AnalysisEditorViewImpl.class)
public class AnalysisPresenter extends BasePresenter<AnalysisEditorViewImpl, MainEventBus> {

	private AnalysisServiceAsync analysisService;
	private SampleTypeServiceAsync sampleTypeService;

	private ParameterServiceAsync parameterService;
	private DepartmentServiceAsync departmentService;

	private IValidator<Analysis> analysisValidator;

	private Map<Long, Parameter> parameterMap;
	private Map<Long, Department> departmentMap;
	private Map<Long, SampleType> sampleTypeMap;

	private String currentSortProperty = "name";
	private Boolean currentSortDesc = false;

	public AnalysisPresenter() {
		analysisValidator = GWT.create(Analysis.class);
		parameterMap = new HashMap<Long, Parameter>();
		departmentMap = new HashMap<Long, Department>();
		sampleTypeMap = new HashMap<Long, SampleType>();
	}

	public void onShowAnalysisManagement(DockLayoutPanel mainWidget) {

		WidgetUtils.removeCenterWidget(mainWidget);
		mainWidget.add(view.getViewWidget());

		refreshChoices();
		refreshData();
	}

	public void bind() {

		bindEditBox();
		addColumns();
		setDelegate();

	}

	private void setDelegate() {
		view.getTable().setDelegate(new Delegate<Analysis>() {

			@Override
			public void onRangeChanged(ListView<Analysis> listView) {

				final Range r = listView.getRange();

				analysisService.getAllAnalysis(r.getStart(), r.getLength(), currentSortProperty, currentSortDesc, new AsyncCall<List<Analysis>>() {

					@Override
					protected void onFailureE(Throwable caught) {
						eventBus.error(caught.getMessage());
					}

					@Override
					protected void onSuccessE(List<Analysis> result) {
						view.getTable().setData(r.getStart(), r.getLength(), result);
						view.getTable().redraw();
					}
				});
			}
		});
	}

	private void addColumns() {
		final SortableHeader nameHeader = new SortableHeader("Name", "name");
		nameHeader.setSorted(true);

		nameHeader.setUpdater(new ValueUpdater<String>() {

			@Override
			public void update(String value) {
				nameHeader.toggleReverseSort();
				currentSortProperty = nameHeader.getPropertyName();
				currentSortDesc = nameHeader.getReverseSort();
				view.getTable().refresh();
				view.getTable().refreshHeaders();
			}
		});

		view.getTable().addColumn(new TextColumn<Analysis>() {

			@Override
			public String getValue(Analysis object) {
				return object.getName();
			}
		}, nameHeader);

		view.getTable().addColumn(new TextColumn<Analysis>() {

			@Override
			public String getValue(Analysis object) {
				if (object.getEstimatedTime() != null) {
					return object.getEstimatedTime().toString();
				}
				return "";
			}
		}, "Est. Time");

		view.getTable().addColumn(new TextColumn<Analysis>() {

			@Override
			public String getValue(Analysis object) {
				if (object.getEstimatedCost() != null) {
					return object.getEstimatedCost().toString();
				}
				return "";
			}
		}, "Est. Cost");

		view.getTable().addColumn(new TextColumn<Analysis>() {

			@Override
			public String getValue(Analysis object) {
				if (object.getDoneIn() != null)
					return object.getDoneIn().getAbbreviation();
				return "";
			}
		}, "Done In");

		view.getTable().addColumn(new TextColumn<Analysis>() {

			@Override
			public String getValue(Analysis object) {
				String parameter = "";

				for (Parameter p : object.getDeterminedParameter()) {
					parameter += p.getName() + ", ";
				}

				if (parameter.length() > 0) {
					parameter = parameter.substring(0, parameter.length() - 2);
				}

				return parameter;
			}
		}, "Parameters");

		view.getTable().addColumn(new Column<Analysis, Boolean>(new CheckboxCell()) {

			@Override
			public Boolean getValue(Analysis object) {
				return object.getActive();
			}
		}, "Active");

		Column<Analysis, String> editColumn = new Column<Analysis, String>(new ButtonCell()) {

			@Override
			public String getValue(Analysis object) {
				return "Edit";
			}
		};

		editColumn.setFieldUpdater(new FieldUpdater<Analysis, String>() {

			@Override
			public void update(int index, Analysis object, String value) {

				view.getEditAnalysisBox().getDepartment().clear();

				for (Department d : departmentMap.values()) {
					view.getEditAnalysisBox().getDepartment().addItem(d.getName());
				}

				List<Parameter> selectedParameters = object.getDeterminedParameter();
				List<Parameter> unselectedParameters = new ArrayList<Parameter>();

				for (Parameter p : parameterMap.values()) {
					if (!selectedParameters.contains(p)) {
						unselectedParameters.add(p);
					}
				}

				view.getEditAnalysisBox().getParameterSelection().setItems(unselectedParameters, selectedParameters);

				List<SampleType> selectedSampleTypes = object.getValidSampleTypes();
				List<SampleType> unselectedSampleTypes = new ArrayList<SampleType>();

				for (SampleType st : sampleTypeMap.values()) {
					if (!selectedSampleTypes.contains(st)) {
						unselectedSampleTypes.add(st);
					}
				}

				view.getEditAnalysisBox().getSampleTypeSelection().setItems(unselectedSampleTypes, selectedSampleTypes);

				view.getEditAnalysisBox().showEdit(object);
			}
		});

		view.getTable().addColumn(editColumn);
	}

	private void bindEditBox() {
		view.getAddAnalysisButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				view.getEditAnalysisBox().getDepartment().clear();

				for (Department d : departmentMap.values()) {
					view.getEditAnalysisBox().getDepartment().addItem(d.getName());
				}

				view.getEditAnalysisBox().getParameterSelection().setItems(parameterMap.values(), new ArrayList<Parameter>());

				view.getEditAnalysisBox().getSampleTypeSelection().setItems(sampleTypeMap.values(), new ArrayList<SampleType>());

				view.getEditAnalysisBox().showNew();
			}
		});

		view.getEditAnalysisBox().getCancelButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				view.getEditAnalysisBox().hide();
			}
		});

		view.getEditAnalysisBox().getSaveButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				if (view.confirmEdit("Do you want to save your changes?"))
					saveAnalysis();
			}
		});
	}

	private void refreshData() {
		analysisService.getAnalysisCount(new AsyncCall<Integer>() {

			@Override
			protected void onFailureE(Throwable caught) {
				eventBus.error(caught.getMessage());
			}

			@Override
			protected void onSuccessE(Integer result) {
				view.getTable().setDataSize(result, true);

				if (result < view.getDefaultPageSize()) {
					view.getPager().setVisible(false);
					view.getTable().setPageSize(result);
				} else {
					view.getPager().setVisible(true);
				}
				view.getTable().refresh();
			}
		});
	}

	private void saveAnalysis() {

		EditAnalysisBox editBox = view.getEditAnalysisBox();
		Analysis tempAnalysis = editBox.getAnalysis();

		if (tempAnalysis == null)
			tempAnalysis = new Analysis();

		tempAnalysis.setName(editBox.getName().getValue());

		try {
			tempAnalysis.setEstimatedTime(Double.parseDouble(editBox.getEstimatedTime().getValue()));
		} catch (NumberFormatException ex) {
			tempAnalysis.setEstimatedTime(null);
		}

		try {
			tempAnalysis.setEstimatedCost(Double.parseDouble(editBox.getEstimatedCost().getValue()));
		} catch (NumberFormatException ex) {
			tempAnalysis.setEstimatedCost(null);
		}

		if (editBox.getDepartment().getSelectedIndex() != -1) {

			Department selectedDepartment = null;

			for (Department d : departmentMap.values()) {
				if (d.getName().equals(editBox.getDepartment().getItemText(editBox.getDepartment().getSelectedIndex()))) {
					selectedDepartment = d;
					break;
				}
			}

			tempAnalysis.setDoneIn(selectedDepartment);
		}

		tempAnalysis.getDeterminedParameter().clear();

		for (Parameter p : editBox.getParameterSelection().getSelectedItems()) {
			tempAnalysis.getDeterminedParameter().add(p);
		}

		tempAnalysis.getValidSampleTypes().clear();

		for (SampleType st : editBox.getSampleTypeSelection().getSelectedItems()) {
			tempAnalysis.getValidSampleTypes().add(st);
		}

		tempAnalysis.setActive(editBox.getActive().getValue());

		Set<InvalidConstraint<Analysis>> analysisErrors = analysisValidator.validate(tempAnalysis);

		if (analysisErrors.size() > 0) {
			for (InvalidConstraint<Analysis> ic : analysisErrors) {
				editBox.showError(ic.getItemName(), ic.getMessage());
			}
			return;
		}

		analysisService.saveOrUpdateAnalysis(tempAnalysis, new AsyncCall<Void>() {

			@Override
			public void onSuccessE(Void result) {

				view.getEditAnalysisBox().hide();
				refreshData();
			}

			@Override
			public void onFailureE(Throwable error) {
				eventBus.error(error.getMessage());
			}
		});
	}

	private void refreshChoices() {

		departmentService.getAllDepartments(new AsyncCall<List<Department>>() {

			@Override
			public void onSuccessE(List<Department> result) {
				departmentMap.clear();

				for (Department d : result) {
					departmentMap.put(d.getId(), d);
				}
			}

			@Override
			public void onFailureE(Throwable error) {
				eventBus.error(error.getMessage());
			}
		});

		parameterService.getAllParameter(new AsyncCall<List<Parameter>>() {

			@Override
			public void onSuccessE(List<Parameter> result) {
				parameterMap.clear();

				for (Parameter p : result) {
					parameterMap.put(p.getId(), p);
				}
			}

			@Override
			public void onFailureE(Throwable error) {
				eventBus.error(error.getMessage());
			}
		});

		sampleTypeService.getAllSampleType(new AsyncCall<List<SampleType>>() {

			@Override
			protected void onFailureE(Throwable caught) {
				eventBus.error(caught.getMessage());
			}

			@Override
			protected void onSuccessE(List<SampleType> result) {
				sampleTypeMap.clear();

				for (SampleType st : result) {
					sampleTypeMap.put(st.getId(), st);
				}
			}
		});
	}

	@InjectService
	public void setAnalysisService(AnalysisServiceAsync analysisService) {
		this.analysisService = analysisService;
	}

	@InjectService
	public void setDepartmentService(DepartmentServiceAsync departmentService) {
		this.departmentService = departmentService;
	}

	@InjectService
	public void setParameterService(ParameterServiceAsync parameterService) {
		this.parameterService = parameterService;
	}

	@InjectService
	public void setSampleTypeService(SampleTypeServiceAsync sampleTypeService) {
		this.sampleTypeService = sampleTypeService;
	}

}
